///////////////////////////////////////////////////////////////////////////////
//    Copyright (c) 2021 CASTest Corporation Limited. All Rights Reserved    //
///////////////////////////////////////////////////////////////////////////////


#include "Patterns.h"

unordered_map<int, int> Patterns::CreateSignalOrderMap(vector<Gate *>& vy_pi_gates,
                                                       vector<string> stil_singal) {

    vector<pair<string, int>> vy_name_index;
    vector<pair<string, int>> stil_name_index;
    unordered_map<int, int>   stil_vy_index_map;

    for (int i = 0; i < vy_pi_gates.size(); i++) {
        vy_name_index.emplace_back(make_pair(vy_pi_gates[i]->GetName(), i));
        stil_name_index.emplace_back(make_pair(stil_singal[i], i));
    }

    sort(vy_name_index.begin(),   vy_name_index.end());
    sort(stil_name_index.begin(), stil_name_index.end());

    for (int i = 0; i < vy_pi_gates.size(); i++) {
        auto vy_singal_index = vy_name_index[i];
        auto stil_singal_index = stil_name_index[i];
        if (vy_singal_index.second != stil_singal_index.second) {
            stil_vy_index_map[stil_singal_index.second] = vy_singal_index.second;
#ifdef DEBUG
            cout << vy_singal_index.first    << " "
                 << stil_singal_index.first  << " "
                 << stil_singal_index.second << " "
                 << vy_singal_index.second   << endl;
#endif
        }
    }
    return stil_vy_index_map;
}

void Patterns::ReadPatternFromStil(const string& stil_file_name,
                                   unordered_map<int, int>& stil_vy_pi_order,
                                   unordered_map<int, int>& stil_vy_po_order) {

    string str;
    string prefix = stil_file_name.substr(0, stil_file_name.rfind(".stil"));
    string path_external_ops = prefix + ".op";
    string path_ofstream_pat = prefix + ".pat";

    ifstream ops(path_external_ops);
    ofstream pat(path_ofstream_pat);

    if (!ops.is_open()) {
        string msg = ".ops file open error";
        cerr << Errors::ErrorsMsg(ErrorType::FILE_NOT_FOUND, msg) << endl;
        exit(1);
    }

    vector<vector<string>> proc;
    vector<string>         scan_structure;
    vector<string>         singe_proc;
    int ptr = -1;

    while (ops >> str) {
        //read in when meet "Call".
        if (str == "Call") {
            ptr++;
            singe_proc.clear();
            while (ops >> str) {
                if (str == "Call") {
                    ptr++;
                    proc.push_back(singe_proc);
                    singe_proc.clear();
                } else
                    singe_proc.push_back(str);
            }
        }
    }
    proc.push_back(singe_proc);

    bool first_load_unload = true;
    set<int> clocks;

    for (int i = 0; i <= ptr; i++) {
        // cout << "procedure: " << i << endl;
        // for(int j=0;j<proc[i].size();j++){
        // 	cout << proc[i][j] << " ";
        // }
        // cout << endl;
        string op = proc[i][0];

        //capture.
        if (op == "capture" || op == "multiclock_capture") {
            vector<size_t> pi_clock_ids;
            string port1 = proc[i][1], pat1 = proc[i][2], port2 = proc[i][3], pat2 = proc[i][4];

            if (port1 == "_pi") {
                Value pi_value;
                vector<Value> pattern_pi(_num_pi);
                for (int j = 0; j < _num_pi; j++) {
                    size_t pi_index = j;
                    if (stil_vy_pi_order.find(j) != stil_vy_pi_order.end()) {
                        pi_index = stil_vy_pi_order[j];
                    }
                    if (pat1[j] == 'P') {

                        pi_clock_ids.push_back(pi_index);
                        pat << "P";
                        if (_clock_start_value.find(pi_index) != _clock_start_value.end()) {
                            if (_clock_start_value[pi_index] == 0) {
                                pi_value.SetSerial0();
                            } else {
                                pi_value.SetSerial1();
                            }
                        } else {
                            pi_value.SetSerial0();
                        }
                    } else if (pat1[j] == '0') {
                        pat << "0";
                        pi_value.SetSerial0();

                    } else {
                        pat << "1";
                        pi_value.SetSerial1();
                    }
                    pattern_pi[pi_index] = pi_value;
                }
                pat << " ";
                _serial_patterns_pi.push_back(pattern_pi);

            } else {
                cout << "Error: capture." << endl;
                exit(1);
            }

            if (port2 == "_po") {
                Value po_value;
                vector<Value> pattern_po(_num_po);
                for (int j = 0; j < _num_po; j++) {
                    size_t po_index = j;
                    if (stil_vy_po_order.find(j) != stil_vy_po_order.end()) {
                        po_index = stil_vy_po_order[j];
                    }
                    string po;
                    if (pat2[j] == 'X') {
                        po = "X";
                        po_value.SetSerialX();
                        pattern_po[po_index] = po_value;
                    } else if (pat2[j] == 'H') {
                        po = "1";
                        po_value.SetSerial1();
                        pattern_po[po_index] = po_value;
                    } else {
                        po = "0";
                        po_value.SetSerial0();
                        pattern_po[po_index] = po_value;
                    }

                    pat << po;
                }
                pat << endl;
                _serial_patterns_po.push_back(pattern_po);

            } else {
                cout << "Error: capture." << endl;
                exit(1);
            }
            _serial_pattern_clock_ids.push_back(pi_clock_ids);
        } else if (op == "load_unload" && i <= ptr - 2) {

            vector<string> load_value_str = proc[i];
            vector<vector<Value>> single_pattern_load_values;
            vector<vector<Value>> single_pattern_unload_values;
            vector<string> unload_value_str;
            if (!first_load_unload && load_value_str.size() == 2 * _num_scan_chain + 1) {
                // last unload value
                break;
            }
            for (size_t j = 0; j < _num_scan_chain; j++) {
                vector<Value> single_scan_chain_load_value;
                vector<Value> single_scan_chain_unload_value;

                if (first_load_unload) {
                    if (j == _num_scan_chain - 1) {
                        first_load_unload = false;

                    }
                    for (auto it : load_value_str[2 + 2 * j]) {
                        Value load_value;
                        if (it == '0') {
                            load_value.SetSerial0();
                        } else if (it == '1') {
                            load_value.SetSerial1();
                        } else {
                            load_value.SetSerialX();
                        }
                        single_scan_chain_load_value.push_back(load_value);
                    }
                } else {
                    for (auto it : load_value_str[_num_scan_chain * 2 + 2 + 2 * j]) {
                        Value load_value;
                        if (it == '0') {
                            load_value.SetSerial0();
                        } else if (it == '1') {
                            load_value.SetSerial1();
                        } else {
                            load_value.SetSerialX();
                        }
                        single_scan_chain_load_value.push_back(load_value);
                    }
                }

                unload_value_str = proc[i + 2];
                for (auto it : unload_value_str[2 * j + 2]) {

                    Value unload_value;
                    if (it == 'L') {
                        unload_value.SetSerial0();
                    } else if (it == 'H') {
                        unload_value.SetSerial1();
                    } else {
                        unload_value.SetSerialX();
                    }
                    single_scan_chain_unload_value.push_back(unload_value);
                }
                single_pattern_load_values.push_back(single_scan_chain_load_value);
                single_pattern_unload_values.push_back(single_scan_chain_unload_value);
            }
            _serial_load_value.push_back(single_pattern_load_values);
            _serial_unload_value.push_back(single_pattern_unload_values);


        }
    }

    ops.close();
    pat.close();
//    cout << "serial pi" << endl;
//    PrintSerialPatternValue(_serial_patterns_pi);
//    cout << "serial po" << endl;
//    PrintSerialPatternValue(_serial_patterns_po);
//    cout << "serial load" << endl;
//    PrintSerialScanValue(_serial_load_value);
//    cout << "serial unload" << endl;
//    PrintSerialScanValue(_serial_unload_value);
    _num_pattern = _serial_patterns_pi.size();
    _serial_pattern_clock_value = vector<vector<Value>>(_num_pattern);
    Value clock_frame2_value;
    for (size_t pattern_id = 0; pattern_id < _num_pattern; pattern_id++) {
        for (size_t clock_pi_id : _clock_pi_ids) {
            if (find(_serial_pattern_clock_ids[pattern_id].begin(), _serial_pattern_clock_ids[pattern_id].end(),
                     clock_pi_id)
                == _serial_pattern_clock_ids[pattern_id].end()) {
                clock_frame2_value = _serial_patterns_pi[pattern_id][clock_pi_id];
            } else {
                if (_clock_start_value.find(clock_pi_id) != _clock_start_value.end()) {
                    if (_clock_start_value[clock_pi_id] == 0) {
                        clock_frame2_value.SetSerial1();
                    } else {
                        clock_frame2_value.SetSerial0();
                    }
                } else {
                    clock_frame2_value.SetSerial1();
                }
            }

            _serial_pattern_clock_value[pattern_id].push_back(clock_frame2_value);

        }
    }

}

void Patterns::PrintSerialPatternValue(const vector<vector<Value>> &patterns) {
    for (auto &pattern : patterns) {
        for (auto &pattern_single_value : pattern) {
            if (pattern_single_value.bit0 == 0x1 && pattern_single_value.bit1 == 0x0) {
                cout << "1 ";
            } else if (pattern_single_value.bit0 == 0x0 && pattern_single_value.bit1 == 0x1) {
                cout << "0 ";
            } else if (pattern_single_value.bit0 == 0x0 && pattern_single_value.bit1 == 0x0) {
                cout << "X ";
            } else {
                cout << "Z ";
            }
        }
        cout << endl;

    }
}

void Patterns::PrintSerialScanValue(const vector<vector<vector<Value>>> &scan_value) {
    for (auto &pattern : scan_value) {
        for (auto &scan_chain_value : pattern) {
            for (auto &pattern_single_value : scan_chain_value) {
                if (pattern_single_value.bit0 == 0x1 && pattern_single_value.bit1 == 0x0) {
                    cout << "1 ";
                } else if (pattern_single_value.bit0 == 0x0 && pattern_single_value.bit1 == 0x1) {
                    cout << "0 ";
                } else if (pattern_single_value.bit0 == 0x0 && pattern_single_value.bit1 == 0x0) {
                    cout << "X ";
                } else {
                    cout << "Z ";
                }
            }

        }
        cout << endl;

    }
}

int Patterns::ConvertSerialToParallelPattern() {
    if (_num_word == 0) {
        _num_word = _num_pattern / SIZE_OF_PACKET;
        if (_num_pattern % SIZE_OF_PACKET != 0) {
            _num_word++;
        }
        _parallel_patterns_pi = vector<vector<Value>>(_num_word, vector<Value>(_num_pi));
        _parallel_patterns_po = vector<vector<Value>>(_num_word, vector<Value>(_num_po));
        Value parallel_value;
        int word_id = 0;
        for (int pattern_id = 0; pattern_id < _num_pattern; pattern_id++) {
            if (pattern_id % SIZE_OF_PACKET == 0 && pattern_id != 0) {
                word_id++;
            }
            for (int gate_id = 0; gate_id < _num_pi; gate_id++) {
                auto gate_value = _serial_patterns_pi[pattern_id][gate_id];
                parallel_value = _parallel_patterns_pi[word_id][gate_id];
                parallel_value.bit0 = (parallel_value.bit0 << 1) + gate_value.bit0;
                parallel_value.bit1 = (parallel_value.bit1 << 1) + gate_value.bit1;
                _parallel_patterns_pi[word_id][gate_id] = parallel_value;
            }

        }

        //TODO: optimize redundant code
        word_id = -1;
        last_parallel_pattern_bit_num = 0;
        for (int pattern_id = 0; pattern_id < _num_pattern; pattern_id++) {
            if (pattern_id % SIZE_OF_PACKET == 0 ) {
                word_id++;
                last_parallel_pattern_bit_num = 0;
            }
            last_parallel_pattern_bit_num++;
            for (int gate_id = 0; gate_id < _num_po; gate_id++) {
                auto gate_value = _serial_patterns_po[pattern_id][gate_id];
                parallel_value = _parallel_patterns_po[word_id][gate_id];
                parallel_value.bit0 = (parallel_value.bit0 << 1) + gate_value.bit0;
                parallel_value.bit1 = (parallel_value.bit1 << 1) + gate_value.bit1;
                _parallel_patterns_po[word_id][gate_id] = parallel_value;
            }

        }

        _parallel_load_value = vector<vector<vector<Value>>>(_num_word, vector<vector<Value>>(_num_scan_chain));
        _parallel_unload_value = vector<vector<vector<Value>>>(_num_word, vector<vector<Value>>(_num_scan_chain));
        vector<int> every_scan_chain_dff_num;
        for (int i = 0; i < _num_scan_chain; i++) {
            every_scan_chain_dff_num.push_back(_serial_load_value[0][i].size());
        }
        for (int word_id = 0; word_id < _num_word; word_id++) {
            for (int scan_chain_id = 0; scan_chain_id < _num_scan_chain; scan_chain_id++) {
                _parallel_load_value[word_id][scan_chain_id].resize(every_scan_chain_dff_num[scan_chain_id]);
                _parallel_unload_value[word_id][scan_chain_id].resize(every_scan_chain_dff_num[scan_chain_id]);

            }
        }
        word_id = -1;

        for (int pattern_id = 0; pattern_id < _num_pattern; pattern_id++) {
            if (pattern_id % SIZE_OF_PACKET == 0 ) {
                word_id++;
            }
            for (auto scan_id = 0; scan_id < _num_scan_chain; scan_id++) {

                for (int gate_id = 0; gate_id < _serial_load_value[pattern_id][scan_id].size(); gate_id++) {
                    auto gate_value = _serial_load_value[pattern_id][scan_id][gate_id];
                    parallel_value = _parallel_load_value[word_id][scan_id][gate_id];
                    parallel_value.bit0 = (parallel_value.bit0 << 1) + gate_value.bit0;
                    parallel_value.bit1 = (parallel_value.bit1 << 1) + gate_value.bit1;
                    _parallel_load_value[word_id][scan_id][gate_id] = parallel_value;

                    gate_value = _serial_unload_value[pattern_id][scan_id][gate_id];
                    parallel_value = _parallel_unload_value[word_id][scan_id][gate_id];
                    parallel_value.bit0 = (parallel_value.bit0 << 1) + gate_value.bit0;
                    parallel_value.bit1 = (parallel_value.bit1 << 1) + gate_value.bit1;
                    _parallel_unload_value[word_id][scan_id][gate_id] = parallel_value;
                }
            }
        }
        auto num_clock_pi = _clock_pi_ids.size();
        _parallel_pattern_clock_value.resize(_num_word, vector<Value>(num_clock_pi));
        word_id = -1;
        for (auto pattern_id = 0; pattern_id < _num_pattern; pattern_id++) {
            if (pattern_id % SIZE_OF_PACKET == 0) {
                word_id++;
            }
            for(int i =0 ; i < num_clock_pi; ++i){
                auto gate_value = _serial_pattern_clock_value[pattern_id][i];
                parallel_value =  _parallel_pattern_clock_value[word_id][i];
                parallel_value.bit0 = (parallel_value.bit0 << 1) + gate_value.bit0;
                parallel_value.bit1 = (parallel_value.bit1 << 1) + gate_value.bit1;
                _parallel_pattern_clock_value[word_id][i] = parallel_value;
            }
        }

    }
    return last_parallel_pattern_bit_num;

}

//void Patterns::GenerateRandomSerialPattern(int num_pattern) {
//    _serial_patterns_pi.clear();
//    default_random_engine e;
//    uniform_int_distribution<unsigned> u(0, 4);
//    Value random_value;
//    _num_pattern = num_pattern;
//    _serial_patterns_pi.resize(_num_pi);
//    for (int pi = 0; pi < _num_pi; pi++){
//        for(int pattern_id = 0; pattern_id < num_pattern; pattern_id++){
//            if(random % 2 == 0){
//                random_value.SetSerial0();
//            } else if(random % 2 == 1){
//                random_value.SetSerial1();
//            }else{
//                cout<<"Generate Random Pattern Fail"<<endl;
//                exit(7);
//            }
//            _serial_patterns_pi[pi].push_back(random_value);
//        }
//    }
//}
//
//void Patterns::GenerateRandomParallelPattern(int num_pattern) {
//    default_random_engine e;
//    uniform_int_distribution<unsigned> u(0, 4);
//    Value random_value;
//    for (int pi = 0; pi < _num_pi; pi++){
//        for(int pattern_id = 0; pattern_id < num_pattern; pattern_id++){
//            random_value.SetSerialX();
//            for(int k = 0;k < SIZE_OF_PACKET;k ++){
//                if(random % 2 == 0){
//                    random_value.bit0  = (random_value.bit0 << 1) + 0;
//                    random_value.bit1  = (random_value.bit1 << 1) + 1;
//
//                } else if(random % 2 == 1){
//                    random_value.bit0  = (random_value.bit0 << 1) + 1;
//                    random_value.bit1  = (random_value.bit1 << 1) + 0;
//                }else{
//                    cout<<"Generate Random Pattern Fail"<<endl;
//                    exit(7);
//                }
//            }
//
//            _serial_patterns_pi[pi].push_back(random_value);
//        }
//    }
//
//}


